package com.sophware {
	import com.yahoo.astra.utils.CMYKColor;
	import com.yahoo.astra.utils.RGBColor;
	
	import flash.utils.ByteArray;
	import flash.utils.Endian;
	
	/**
	 * A class with two static functions to handle Adobe Swatch Exchange(ASE) files.  One to read an ASE file (as
	 * a ByteArray), and one to write an ASE file (also as a ByteArray).  Currently only RGB and CMYK values are
	 * supported, other color formats will stop the file from being read further.  Colors to be written are placed
	 * in an array of either CMYKColor or RGBColor objects from Yahoo!'s Astra libraries
	 * (http://developer.yahoo.com/flash/astra-flex/).
	 * 
	 * @author Aaron Leavitt
	 * 
	 */	
	public class ASEUtil {
		/**
		 * Reads and parses an ASE file.  File should be read into a ByteArray prior to passing it in.  Note that
		 * color formats other than RGB and CMYK will currently stop the file processing immediately and the colors
		 * that have been read already will be returned.
		 * 
		 * @param inBytes The ASE file as a ByteArray.
		 * @return a PalletModel object with the name of the palette if provided, and colors in an ArrayCollection.
		 * 
		 */				
		public static function readASE(inBytes:ByteArray):PaletteModel {
			var newPalette:PaletteModel = new PaletteModel();
			
			inBytes.endian = Endian.BIG_ENDIAN;
			var tempNum:Number = inBytes.bytesAvailable;
			
			if(inBytes.readUTFBytes(4) != "ASEF") {
				throw new Error("Bad file format");
			}
			if(inBytes.readShort()+"."+inBytes.readShort() != "1.0") {
				trace("Warning, ASE version may be incompatible.");
			}
			
			inBytes.readInt(); //Move past block number info;
			
			//Detect if we have a group start, or a color start
			var blockStart:int = inBytes.readShort();
			if(blockStart == -16383) {
				//Group start
				inBytes.readInt(); //Skip the block size, we'll use the character count instead.
				var nameLen:Number = inBytes.readShort(); //Length of the name to read in
				
				newPalette.name = inBytes.readMultiByte(nameLen*2,"unicodeFFFE");
				//inBytes.readShort(); //Gets us past the name terminator
				blockStart = inBytes.readShort();
			}
			
			var tempRGB:RGBColor;
			var tempCMYK:CMYKColor;
			
			while(blockStart == 1) {
				inBytes.readInt();  //Move past block count
				var stringLen:int = inBytes.readShort();
				inBytes.readMultiByte(stringLen*2,"unicodeFFFE"); //Skip color name
				
				var kind:String = inBytes.readUTFBytes(4);
				if(kind == "RGB ") {
					tempRGB = new RGBColor();
					tempRGB.red = inBytes.readFloat()*255;
					tempRGB.green = inBytes.readFloat()*255;
					tempRGB.blue = inBytes.readFloat()*255;
					
					newPalette.colors.addItem(tempRGB);
					inBytes.readShort(); //Move past close of color
					
					//Some ASE files don't close out correctly, making sure we don't drop off the end.
					try{
						blockStart = inBytes.readShort();
					}
					catch(err:Error) {
						blockStart = 0;
					}
				}
				else if(kind == "CMYK") {
					tempCMYK = new CMYKColor();
					tempCMYK.cyan = inBytes.readFloat()*100;
					tempCMYK.magenta = inBytes.readFloat()*100;
					tempCMYK.yellow = inBytes.readFloat()*100;
					tempCMYK.key = inBytes.readFloat()*100;
					
					newPalette.colors.addItem(tempCMYK);
					inBytes.readShort(); //Move past close of color
					
					//Some ASE files don't close out correctly, making sure we don't drop off the end.
					try{
						blockStart = inBytes.readShort();
					}
					catch(err:Error) {
						blockStart = 0;
					}
				}
				else {
					trace("Unsupported color format of "+kind);
					blockStart = 0;
				}
				trace("Start is: "+blockStart); //Still some issues with block starts, tracing for debugging
			}
			
			return newPalette;
		}
		
		/**
		 * Used to take a string and an array of IColor objects and turn them into an ASE formatted ByteArray.  One
		 * single group with the name provided will be written.  CMYK and RGB are currently the only color formats
		 * supported.
		 *  
		 * @param name Name of the palette group to write out.
		 * @param colors Array of CMYKColor or RGBColor objects to write out.
		 * @return ASE formatted ByteArray, ready to be output to a file.
		 * 
		 */		
		public static function writeASE(name:String,colors:Array):ByteArray {
			var outBytes:ByteArray = new ByteArray();
			//Write file signature
			outBytes.writeUTFBytes("ASEF");
			
			//Write ASE version
			outBytes.writeShort(1);
			outBytes.writeShort(0);
			
			//Write block count
			outBytes.writeInt(colors.length+2);
			
			//Write group start
			outBytes.writeByte(0xC0);
			outBytes.writeByte(0x01);
			
			//Write block length
			outBytes.writeInt(4+name.length*2);
			
			//Write character count
			outBytes.writeShort(name.length+1);
			
			//Write group name
			outBytes.writeMultiByte(name,"unicodeFFFE");
			outBytes.writeShort(0);
			
			//Loop through colors
			for(var i:int=0; i<colors.length; i++) {
				//Color Block start
				outBytes.writeShort(1);
				
				if(colors[i] is RGBColor) {
					//Block length
					outBytes.writeInt(22);
					
					outBytes.writeShort(1);
					outBytes.writeShort(0);
					
					//Write color value
					outBytes.writeUTFBytes("RGB ");
					outBytes.writeFloat(colors[i].red/255);
					outBytes.writeFloat(colors[i].green/255);
					outBytes.writeFloat(colors[i].blue/255);
				}
				else if(colors[i] is CMYKColor) {
					//Block length
					outBytes.writeInt(26);
					
					outBytes.writeShort(1);
					outBytes.writeShort(0);
					
					outBytes.writeUTFBytes("CMYK");
					outBytes.writeFloat(colors[i].cyan/100);
					outBytes.writeFloat(colors[i].magenta/100);
					outBytes.writeFloat(colors[i].yellow/100);
					outBytes.writeFloat(colors[i].key/100);
				}
				
				//Write color type (2 is normal)
				outBytes.writeShort(2);
			}			
			
			//Write group end block
			outBytes.writeByte(0xC0);
			outBytes.writeByte(0x02);
			outBytes.writeInt(0);
			
			return outBytes;
		}
	}
}